package com.lemon.boot.common.base;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lemon.boot.common.enums.StatusEnum;
import com.lemon.boot.common.enums.TimeEnum;
import com.lemon.boot.common.properties.TenantConfigProperties;
import com.lemon.boot.common.service.FileService;
import com.lemon.boot.common.utils.ConverterUtil;
import com.lemon.boot.common.web.Result;
import lombok.extern.log4j.Log4j2;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author 李猛
 * @datetime 2023/4/15 17:18
 * @description 公共的Service实现类
 */
@Log4j2
public abstract class BaseServiceImpl<M extends BaseMapper<P>, P, D> extends ServiceImpl<M, P> implements BaseService<P, D> {
    @Resource
    private TenantConfigProperties tenantConfigProperties;
    @Resource
    private FileService fileService;

    @Override
    public String add(P p) {
        return this.save(p) ? "添加成功！" : "添加失败！";
    }

    @Override
    @Transactional
    public String adds(List<P> ps) {
        return this.saveBatch(ps, Integer.MAX_VALUE) ? "添加成功！" : "添加失败！";
    }

    @Override
    public String del(Long id) {
        return this.removeById(id) ? "删除成功！" : "删除失败！";
    }

    @Override
    @Transactional
    public String del(Collection<Long> ids) {
        return this.removeBatchByIds(ids, Integer.MAX_VALUE) ? "删除成功！" : "删除失败！";
    }

    @Override
    public String edit(P p) {
        return this.updateById(p) ? "修改成功！" : "修改失败！";
    }

    @Override
    @Transactional
    public String edits(List<P> ps) {
        return this.updateBatchById(ps, Integer.MAX_VALUE) ? "修改成功！" : "修改失败！";
    }

    @Override
    @Transactional
    public String addOrEdit(P p) {
        //1.判断是添加还是修改
        String type = this.hasId(p) ? "修改" : "添加";

        //2.执行添加或修改
        boolean result = this.saveOrUpdate(p);

        //3.返回
        return result ? type + "成功！" : type + "失败！";
    }

    @Override
    public P get(Long id) {
        return this.baseMapper.selectById(id);
    }

    @Override
    public List<P> get(Collection<Long> ids) {
        return this.baseMapper.selectBatchIds(ids);
    }

    @Override
    public Page<P> map(Map<String, Object> map) {
        //1.封装分页数据
        Page<P> page = this.getCurrentAndPage(map);

        //2.封装请求参数
        QueryWrapper<P> wrapper = mapToEqWrapper(map);

        //3.查询分页数据
        return this.baseMapper.selectPage(page, wrapper);
    }

    @Override
    public List<?> list(D d) {
        //1.获取map参数
        Map<String, Object> map = toMap(d);

        //2.封装请求参数
        QueryWrapper<P> wrapper = mapToLikeWrapper(map);

        //3.查询集合
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public Page<?> page(D d) {
        //1.获取map参数
        Map<String, Object> map = toMap(d);

        //2.分页数据
        Page<P> page = this.getCurrentAndPage(d);

        //3.封装请求参数（模糊模糊匹配）
        QueryWrapper<P> wrapper = mapToLikeWrapper(map);

        //4.查询分页数据
        return this.baseMapper.selectPage(page, wrapper);
    }

    @Override
    public long count(D d) {
        //1.获取map参数
        Map<String, Object> map = toMap(d);

        //2.封装请求参数
        QueryWrapper<P> wrapper = mapToEqWrapper(map);

        //3.查询个数
        return this.count(wrapper);
    }

    @Override
    public boolean exist(D d) {
        //1.获取map参数
        Map<String, Object> map = toMap(d);

        //2.封装请求参数
        QueryWrapper<P> wrapper = mapToEqWrapper(map);

        //3.判断是否存在
        return this.exists(wrapper);
    }

    @Override
    public String enable(Collection<Long> ids) {
        boolean result = this.update().set(StatusEnum.ENABLE.getColumn(), StatusEnum.ENABLE.getStatus())
                .set(TimeEnum.UPDATE.getColumn(), LocalDateTime.now()).in("id", ids).update();
        return result ? "启用成功！" : "启用失败！";
    }

    @Override
    public String unable(Collection<Long> ids) {
        boolean result = this.update().set(StatusEnum.UNABLE.getColumn(), StatusEnum.UNABLE.getStatus())
                .set(TimeEnum.UPDATE.getColumn(), LocalDateTime.now()).in("id", ids).update();
        return result ? "停用成功！" : "停用失败！";
    }

    @Override
    public Result<?> upload(MultipartFile file) {
        return Result.ok(fileService.localUpload(file));
    }

    @Override
    public void export(D d) {
        log.error("开发同学，导出方法需要重写哈！！！！");
    }

    /**
     * DTO 转 Map
     *
     * @param d
     * @return
     */
    private Map<String, Object> toMap(D d) {
        return ConverterUtil.toMap(d).entrySet().stream()
                .filter(item -> !item.getKey().equals("serialVersionUID"))
                .filter(item -> Objects.nonNull(item.getValue()))
                .filter(item -> StringUtils.hasText(Objects.toString(item.getValue())))
                .collect(Collectors.toMap(item -> StrUtil.toUnderlineCase(item.getKey()), Map.Entry::getValue));
    }

    /**
     * 获取分页参数
     *
     * @param map
     * @return
     */
    private Page<P> getCurrentAndPage(Map<String, Object> map) {
        //1.封装分页数据
        Page<P> page = new Page<>();
        if (!ObjectUtil.isEmpty(map.get("current"))) {
            page.setCurrent(NumberUtil.parseLong(map.get("current").toString()));
        }
        if (!ObjectUtil.isEmpty(map.get("size"))) {
            page.setSize(NumberUtil.parseLong(map.get("size").toString()));
        }
        return page;
    }

    /**
     * 获取分页参数
     *
     * @param d
     * @return
     */
    private Page<P> getCurrentAndPage(D d) {
        //分页数据
        long current = 0, size = 10;
        try {
            Method currentMethod = d.getClass().getSuperclass().getMethod("getCurrent");
            Method sizeMethod = d.getClass().getSuperclass().getMethod("getSize");
            Object currentObj = currentMethod.invoke(d);
            Object sizeObj = sizeMethod.invoke(d);
            current = NumberUtil.parseLong(currentObj.toString());
            size = NumberUtil.parseLong(sizeObj.toString());
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
                 InvocationTargetException e) {
            log.error("BaseServiceImpl.page.message:{},当前页:{},分页数量:{}", e.getMessage(), current, size);
        }
        return new Page<>(current, size);
    }

    /**
     * Map 转 QueryWrapper
     *
     * @param map
     * @return
     */
    private QueryWrapper<P> mapToEqWrapper(Map<String, Object> map) {
        QueryWrapper<P> wrapper = new QueryWrapper<>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            if (!"current".equals(key) && !"size".equals(key)) {
                wrapper.eq(key, entry.getValue());
            }
        }
        return wrapper;
    }

    /**
     * Map 转 QueryWrapper
     *
     * @param map
     * @return
     */
    private QueryWrapper<P> mapToLikeWrapper(Map<String, Object> map) {
        List<String> selectLikeColumn = tenantConfigProperties.getSelectLikeColumn();
        QueryWrapper<P> wrapper = new QueryWrapper<>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            if (!"current".equals(key) && !"size".equals(key)) {
                if (selectLikeColumn.contains(key)) {
                    wrapper.like(key, entry.getValue());
                } else {
                    wrapper.eq(key, entry.getValue());
                }
            }
        }
        return wrapper;
    }

    /**
     * 判断实体类里面的ID是否存在
     *
     * @param p
     * @return
     */
    private boolean hasId(P p) {
        //1.获取所有的属性
        Field[] fields = p.getClass().getDeclaredFields();

        //2.id属性
        String idFieldName = null;

        for (Field field : fields) {
            //3.设置字段是否可访问
            boolean accessible = field.isAccessible();
            if (!accessible) {
                field.setAccessible(true);
            }

            //4.获取 TableId 注解
            TableId annotation = field.getAnnotation(TableId.class);
            if (annotation != null) {
                idFieldName = field.getName();
                break;
            }
        }

        //5.判断是否有ID属性
        if (!StringUtils.hasText(idFieldName)) {
            return false;
        }

        //6.获取 getter方法
        String firstLetter = idFieldName.substring(0, 1).toUpperCase();
        String getterMethod = "get" + firstLetter + idFieldName.substring(1);

        try {
            //7.通过 getter 方法获取值
            Method idMethod = p.getClass().getMethod(getterMethod);
            Object idObj = idMethod.invoke(p);
            return !ObjectUtil.isEmpty(idObj);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
                 InvocationTargetException e) {
            log.error("BaseServiceImpl.hasId.message:{}", e.getMessage());
            return false;
        }
    }
}
